package com.jctrip.hotel.source.service.meituan.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jctrip.hotel.common.enums.OrderStatusEnum;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.domain.dao.HtOrderSourceRecordDao;
import com.jctrip.hotel.domain.entity.HtHotelMthdomInfo;
import com.jctrip.hotel.domain.entity.HtOrderSourceRecord;
import com.jctrip.hotel.domain.entity.HtRoomtypeMthdomInfo;
import com.jctrip.hotel.source.client.MeituanClient;
import com.jctrip.hotel.source.model.OrderDataResult;
import com.jctrip.hotel.source.model.PriceCheckParam;
import com.jctrip.hotel.source.model.meituan.MeituanEnum;
import com.jctrip.hotel.source.model.meituan.req.*;
import com.jctrip.hotel.source.model.meituan.resp.*;
import com.jctrip.hotel.source.model.wehotel.req.OrderData;
import com.jctrip.hotel.source.mq.PoiCallbackMQProducer;
import com.jctrip.hotel.source.service.HotelCommonTaskService;
import com.jctrip.hotel.source.service.basic.HtHotelMthdomInfoService;
import com.jctrip.hotel.source.service.basic.HtRoomtypeMthdomInfoService;
import com.jctrip.hotel.source.service.meituan.MeituanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author: zxy
 * @CreateTime: 2023-12-25 18:08
 * @Description:
 */
@Service
@Slf4j
public class MeituanServiceImpl implements MeituanService {

    private static final BigDecimal hundred = new BigDecimal("100");
    @Autowired
    private MeituanClient meituanClient;
    @Autowired
    private HotelCommonTaskService hotelCommonTaskService;
    @Autowired
    private HtRoomtypeMthdomInfoService htRoomtypeMthdomInfoService;
    @Autowired
    private HtOrderSourceRecordDao htOrderSourceRecordDao;
    @Autowired
    private HtHotelMthdomInfoService htHotelMthdomInfoService;
    @Autowired
    private PoiCallbackMQProducer poiCallbackMQProducer;

    @Override
    public String parseHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        if (!innId.startsWith(SourceEnum.MTHDOM.getCode())) {
            return innId;
        }
        return innId.substring(SourceEnum.MTHDOM.getCode().length());
    }

    @Override
    public String parseInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.MTHDOM.getCode() + hotelId;
    }

    @Override
    public String getProduct(String hotelId, String roomTypeCode, String sourceId) {
        return null;
    }

    @Override
    public PoiListResp poiListMeituan(PoiListReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.POI_LIST.getCode(), req, PoiListResp.class);
    }

    @Override
    public GoodsRpResp goodsRpMeituan(GoodsRpReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.GOODS_RP.getCode(), req, GoodsRpResp.class);
    }

    @Override
    public ContentResp contentMeituan(ContentReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.CONTENT.getCode(), req, ContentResp.class);
    }

    @Override
    public RealroomContentResp realroomContentMeituan(RealroomContentReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.REALROOM_CONTENT.getCode(), req, RealroomContentResp.class);
    }

    @Override
    public OrderCheckResp orderCheckMeituan(OrderCheckReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.ORDER_CHECK.getCode(), req, OrderCheckResp.class);
    }

    @Override
    public OrderBookingResp orderBookingMeituan(OrderBookingReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.ORDER_BOOKING.getCode(), req, OrderBookingResp.class);
    }

    @Override
    public OrderCancelResp orderCancelMeituan(OrderCancelReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.ORDER_CANCEL.getCode(), req, OrderCancelResp.class);
    }

    @Override
    public OrderQueryResp orderQueryMeituan(OrderQueryReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.ORDER_QUERY.getCode(), req, OrderQueryResp.class);
    }

    @Override
    public OrderReminderResp orderReminderMeituan(OrderReminderReq req, Integer partnerId, String accesskey, String secretKey) {
        return getBaseRequest(partnerId, accesskey, secretKey, MeituanEnum.ORDER_REMINDER.getCode(), req, OrderReminderResp.class);
    }

    @Override
    public OrderDataResult postOrder(OrderBookingReq req, Integer partnerId, String accesskey, String secretKey, PriceCheckParam priceCheck) {
        // 下单前进行一波试单
        OrderCheckReq checkReq = OrderCheckReq.builder()
                .hotelId(req.getHotelId())
                .goodsId(req.getGoodsId())
                .checkinDate(req.getCheckinDate())
                .checkoutDate(req.getCheckoutDate())
                .roomNum(req.getRoomNum())
                .totalPrice(req.getTotalPrice())
                .build();
        OrderCheckResp orderCheckResp;
        try {
            orderCheckResp = orderCheckMeituan(checkReq, partnerId, accesskey, secretKey);
        } catch (Exception e) {
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), "试单失败");
            throw new RuntimeException(e.getMessage());
        }
        if (Objects.isNull(orderCheckResp)) {
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), "校验信息为空");
            throw new RuntimeException("校验信息为空");
        }
        if (orderCheckResp.getCode() != 0) {
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), orderCheckResp.getDesc());
            throw new RuntimeException(orderCheckResp.getDesc());
        }
        // 最新价格信息列表，code=0或8时会返回。此处返回价格是单天单间，如多天多间下单时的价格需要计算
        AtomicInteger totalAdvanceRate = new AtomicInteger(0);
        AtomicReference<BigDecimal> raisedTotalPrice = new AtomicReference<>(new BigDecimal("0"));
        orderCheckResp.getPriceModels().forEach(priceModel -> {
            totalAdvanceRate.set(totalAdvanceRate.get() + priceModel.getSalePrice());
            raisedTotalPrice.set(raisedTotalPrice.get().add(
                    priceCheck.getRaiseFunc().apply(
                            new BigDecimal(String.valueOf(priceModel.getSalePrice())).divide(hundred))));
        });

        req.setTotalPrice(totalAdvanceRate.get() * req.getRoomNum());
        if (priceCheck.getTotalRate().multiply(hundred).intValue() <
                req.getTotalPrice() - priceCheck.getTolerancePrice().multiply(hundred).intValue()) {
            String error = String.format("订单销售价不能低于采购总价，订单销售价=%s，采购总价=%s, 容差金额=%s, 目前差额=%s",
                    priceCheck.getTotalRate(),
                    req.getTotalPrice(),
                    priceCheck.getTolerancePrice(),
                    req.getTotalPrice() - priceCheck.getTotalRate().multiply(hundred).intValue());
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), error);
            log.error(error);
            throw new RuntimeException("订单销售价不能低于采购总价");
        }
        OrderBookingResp resp;
        try {
            resp = orderBookingMeituan(req, partnerId, accesskey, secretKey);
        } catch (Exception e) {
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), "下单失败");
            throw new RuntimeException(e.getMessage());
        }
        /**
         * 下单结果码：
         * 0-下单成功；
         * 1-系统繁忙，请稍后重试；
         * 2-产品已变价；
         * 3-重复订单，表示该订单之前已请求下单，可通过查询订单接口同步订单状态；
         * 4-产品已售完，扣减库存失败；
         * 5-账户扣款失败；
         * 10-产品被拉黑, 不允许购买；
         * 11-该产品如选择预计到店时间在 XXXX 之后的，需支付相应担保金，第一期暂不支持下担保产品的订单!（暂未接入）；
         * 12-产品规则变化；
         * 13-身份证未填写或格式不正确；
         * 20-其它
         */
        Integer orderStatus;
        if (resp.getCode() == null) {
            hotelCommonTaskService.insertOrderErrorLog(req.getDistributorOrderId(), JsonUtil.toString(req), "订单状态码为空");
            throw new RuntimeException("订单状态码为空");
        }
        switch (resp.getCode()) {
            case 0:
                orderStatus = 1;
                break;
            default:
                orderStatus = 0;
                break;
        }
        return OrderDataResult.builder()
                .orderState(orderStatus)
                .orderCode(String.valueOf(resp.getMtOrderId()))
                .externalId(req.getDistributorOrderId())
                .totalRate(new BigDecimal(req.getTotalPrice()).divide(hundred))
                .requestJsonStr(JsonUtil.toString(req))
                .responseJsonStr(JsonUtil.toString(resp))
                .build();
    }

    @Override
    public void cancelOrder(OrderCancelReq req, Integer partnerId, String accesskey, String secretKey) {
        if (StringUtils.isBlank(req.getDistributorOrderId()) || Objects.isNull(req.getMtOrderId())) {
            throw new RuntimeException("订单编号为空");
        }
        OrderCancelResp resp = orderCancelMeituan(req, partnerId, accesskey, secretKey);

        if (0 != resp.getCode()) {
            throw new RuntimeException(resp.getDesc());
        }
    }

    @Override
    public OrderData queryOrder(OrderQueryReq req, Integer partnerId, String accesskey, String secretKey) {
        if (CollectionUtils.isEmpty(req.getQueryParams())) {
            throw new RuntimeException("订单编号为空");
        }
        OrderQueryResp resp = orderQueryMeituan(req, partnerId, accesskey, secretKey);
        if (resp.getCode() == 1) {
            return null;
        }
        OrderData orderData = new OrderData();
        OrderQueryReq.QueryParam queryParam = req.getQueryParams().get(0);
        orderData.setOrderCode(String.valueOf(queryParam.getMtOrderId()));
        orderData.setExternalId(queryParam.getDistributorOrderId());
        if (resp.getCode() != 0) {
            orderData.setOrderState(OrderStatusEnum.BOOK_FAILED.getCode());
            return orderData;
        }

        OrderQueryResp.BaseInfo baseInfo = resp.getOrderInfos().get(0).getBaseInfo();
        orderData.setOrderState(getOrderStatus(baseInfo.getOrderStatus()));
        return orderData;
    }

    @Override
    @Async("meituanOrderCallbackTaskAsyncPool")
    public void orderCallback(MeituanReq req) {
        log.info("order-callback={}", JsonUtil.toString(req));
        OrderCallbackReq callbackReq = JsonUtil.toObject(OrderCallbackReq.class, req.getData());
        log.info("order-req = {}", JsonUtil.toString(callbackReq));
        if (Objects.isNull(callbackReq) || StringUtils.isBlank(callbackReq.getDistributorOrderId())) {
            return;
        }

        HtOrderSourceRecord sourceRecord = selectSourceRecord(callbackReq.getDistributorOrderId());

        if (Objects.isNull(sourceRecord)) {
            return;
        }

        sourceRecord.setSourceOrderStatus(getOrderStatus(callbackReq.getOrderStatus()));
        htOrderSourceRecordDao.updateById(sourceRecord);
    }

    @Override
    @Async("taskAsyncPool")
    public void poiCallback(MeituanReq req) {
        log.info("poi-callback={}", JsonUtil.toString(req));
        PoiCallbackReq poiCallbackReq = JsonUtil.toObject(PoiCallbackReq.class, req.getData());
        log.info("poi-req = {}", JsonUtil.toString(poiCallbackReq));
        if (Objects.isNull(poiCallbackReq) || CollectionUtils.isEmpty(poiCallbackReq.getChangeInfos())) {
            return;
        }
        List<HotelChangeParam> hotelIds = new ArrayList<>();
        poiCallbackReq.getChangeInfos().forEach(poi -> {
            String hotelId = parseInnId(String.valueOf(poi.getHotelId()));
            HtHotelMthdomInfo htHotelMthdomInfo = selectHotel(hotelId);
            if (Objects.isNull(htHotelMthdomInfo)) {
                htHotelMthdomInfo = new HtHotelMthdomInfo();
                htHotelMthdomInfo.setHotelId(hotelId);
                htHotelMthdomInfo.setHotelName("newHotel");
                htHotelMthdomInfo.setHotelAddress("newHotel");
                htHotelMthdomInfo.setSourceStatus(1);
                htHotelMthdomInfo.setBookStatus(1);
                if (poi.getChangeTypes().stream().anyMatch(e -> e.equals(1))) {
                    try {
                        htHotelMthdomInfoService.save(htHotelMthdomInfo);
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                }
                syncHotelStaticInfo(hotelId);
                HotelChangeParam param = new HotelChangeParam();
                param.setHotelId(htHotelMthdomInfo.getHotelId());
                param.setStatus(1);
                hotelIds.add(param);
            }
            if (poi.getChangeTypes().stream().anyMatch(e -> e.equals(2))) {
                HotelChangeParam param = new HotelChangeParam();
                param.setHotelId(htHotelMthdomInfo.getHotelId());
                param.setStatus(0);
                hotelIds.add(param);
                htHotelMthdomInfo.setSourceStatus(0);
                htHotelMthdomInfoService.updateById(htHotelMthdomInfo);
            }
        });
        if (CollectionUtils.isEmpty(hotelIds)) {
            return;
        }
        String msg = JsonUtil.toString(hotelIds);
        if (StringUtils.isNotBlank(msg)) {
            poiCallbackMQProducer.send(msg);
        }
    }

    @Override
    @Async("taskAsyncPool")
    public void goodCallback(MeituanReq req) {
        log.info("good-callback={}", JsonUtil.toString(req));
        GoodCallbackReq goodCallbackReq = JsonUtil.toObject(GoodCallbackReq.class, req.getData());
        log.info("good-req = {}", JsonUtil.toString(goodCallbackReq));
    }

    @Override
    public Integer getOrderStatus(Integer orderStatus) {
        /**
         * 0-待确认；1-预定成功；2-已取消；3-预定未到；4-已入住；5-已离店；6-预定失败
         *
         * 10 创建订单 create
         * 20 预定中 booking
         * 21 预定成功 book_suc
         * 22 预定失败 book_fail
         * 30 取消中 canceling
         * 31 取消成功 cancel_suc
         * 32 取消失败 cancel_fail
         * 40 已消费退款 abort（美团客服介入后才可能出现此状态）
         * 50 已入住 book_checkin
         */
        if (Objects.isNull(orderStatus)) {
            return 0;
        }
        Integer status = null;
        switch (orderStatus) {
            case 10:
            case 20:
                status = 0;
                break;
            case 21:
            case 30:
            case 32:
                status = 1;
                break;
            case 31:
            case 40:
                status = 2;
                break;
            case 50:
                status = 4;
                break;
            case 22:
                status = 6;
                break;
            default:
                break;
        }
        return status;
    }

    private void syncHotelStaticInfo(String hotelId) {
        try {
            List<Long> hotelIds = new ArrayList<>();
            hotelIds.add(Long.parseLong(parseHotelId(hotelId)));
            List<String> include = new ArrayList<>();
            include.add("HotelContent.baseInfo");
            include.add("HotelContent.detailInfo");
            include.add("HotelContent.notices");
            include.add("HotelContent.policy");
            include.add("HotelContent.facilities");
            include.add("HotelContent.images");
            include.add("HotelContent.roomBaseInfos");
            ContentReq contentReq = ContentReq.builder()
                    .hotelIds(hotelIds)
                    .include(include)
                    .build();
            ContentResp contentResp = contentMeituan(contentReq, 17281, "9ecfd0e672fbc3440c9902cb8a855db2", "e87b7ab866c73bf36b8f3408e179ef5b");
            HtHotelMthdomInfo htHotelMthdomInfo = setHtHotelMthdomInfo(contentResp);
            if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(htHotelMthdomInfo.getHotelId())) {
                log.error("hotelId {} is null", hotelId);
                return;
            }
            htHotelMthdomInfoService.saveOrUpdate(htHotelMthdomInfo, new LambdaQueryWrapper<HtHotelMthdomInfo>()
                    .eq(HtHotelMthdomInfo::getHotelId, htHotelMthdomInfo.getHotelId()));
            RealroomContentResp realroomContentResp =
                    realroomContentMeituan(
                            RealroomContentReq.builder()
                                    .hotelId(Long.parseLong(parseHotelId(hotelId)))
                                    .build()
                            , 17281, "9ecfd0e672fbc3440c9902cb8a855db2", "e87b7ab866c73bf36b8f3408e179ef5b");
            realroomContentResp.getRoomContents().forEach(e -> {
                if (Objects.nonNull(e.getBaseInfo())
                        && Objects.nonNull(e.getBaseInfo().getRealRoomId())) {
                    HtRoomtypeMthdomInfo htRoomtypeMthdomInfo = setHtRoomtypeMthdomInfo(e);
                    if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(htRoomtypeMthdomInfo.getRoomTypeCode())) {
                        return;
                    }
                    htRoomtypeMthdomInfoService.saveOrUpdate(htRoomtypeMthdomInfo,
                            new LambdaQueryWrapper<HtRoomtypeMthdomInfo>()
                                    .eq(HtRoomtypeMthdomInfo::getHotelId, htRoomtypeMthdomInfo.getHotelId())
                                    .eq(HtRoomtypeMthdomInfo::getRoomTypeCode, htRoomtypeMthdomInfo.getRoomTypeCode()));
                }
            });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private HtHotelMthdomInfo setHtHotelMthdomInfo(ContentResp contentResp) {
        HtHotelMthdomInfo htHotelMthdomInfo = new HtHotelMthdomInfo();
        contentResp.getHotelContents().forEach(e -> {
            htHotelMthdomInfo.setHotelId(parseInnId(String.valueOf(e.getHotelId())));
            ContentResp.BaseInfo baseInfo = e.getBaseInfo();
            ContentResp.DetailInfo detailInfo = e.getDetailInfo();
            if (Objects.isNull(baseInfo) || Objects.isNull(detailInfo)) {
                return;
            }

            htHotelMthdomInfo.setHotelName(baseInfo.getName());
            htHotelMthdomInfo.setHotelNameEn(baseInfo.getNameEn());

            ContentResp.Group group = detailInfo.getGroup();
            htHotelMthdomInfo.setGroupCode(Objects.nonNull(group) ? group.getCode() : null);
            htHotelMthdomInfo.setGroupName(Objects.nonNull(group) ? group.getName() : null);


            ContentResp.Brand brand = detailInfo.getBrand();
            htHotelMthdomInfo.setBrandCode(Objects.nonNull(brand) ? brand.getCode() : null);
            htHotelMthdomInfo.setBrandName(Objects.nonNull(brand) ? brand.getName() : null);

            ContentResp.Address address = baseInfo.getAddress();
            if (Objects.isNull(address)) {
                return;
            }

            ContentResp.AddressLine addressLine = address.getAddressLine();
            htHotelMthdomInfo.setHotelAddress(Objects.nonNull(addressLine) ? addressLine.getName() : null);
            htHotelMthdomInfo.setHotelAddressEn(Objects.nonNull(addressLine) ? addressLine.getNameEn() : null);
            htHotelMthdomInfo.setHotelTel(baseInfo.getPhone());

            ContentResp.Country country = address.getCountry();
            htHotelMthdomInfo.setCountryCode(Objects.nonNull(country) ? String.valueOf(country.getCode()) : null);
            htHotelMthdomInfo.setCountryName(Objects.nonNull(country) ? country.getName() : null);

            ContentResp.Province province = address.getProvince();
            htHotelMthdomInfo.setProvinceCode(Objects.nonNull(province) ? String.valueOf(province.getCode()) : null);
            htHotelMthdomInfo.setProvinceName(Objects.nonNull(province) ? province.getName() : null);

            ContentResp.City city = address.getCity();
            htHotelMthdomInfo.setCityCode(Objects.nonNull(city) ? String.valueOf(city.getCode()) : null);
            htHotelMthdomInfo.setCityName(Objects.nonNull(city) ? city.getName() : null);

            ContentResp.Area area = address.getArea();
            htHotelMthdomInfo.setDistrictCode(Objects.nonNull(area) ? String.valueOf(area.getCode()) : null);
            htHotelMthdomInfo.setDistrictName(Objects.nonNull(area) ? area.getName() : null);
            htHotelMthdomInfo.setIntroduction(detailInfo.getDescription());
            htHotelMthdomInfo.setSourceStatus(baseInfo.getBookable() ? 1 : 0);
            htHotelMthdomInfo.setOverseas(Objects.nonNull(country) && country.getCode() == 10000001 ? 0 : 1);

            ContentResp.Policy policy = e.getPolicy();
            if (Objects.isNull(policy)) {
                return;
            }
            ContentResp.GuestPolicy guestPolicy = policy.getGuestPolicy();
            ContentResp.BreakfastPolicy breakfastPolicy = policy.getBreakfastPolicy();
            htHotelMthdomInfo.setSupportForeignGuest(Objects.nonNull(guestPolicy) && Objects.nonNull(guestPolicy.getGuestType()) ?
                    guestPolicy.getGuestType().getCode() : null);
            htHotelMthdomInfo.setSupportRestaurant(Objects.nonNull(breakfastPolicy) ? breakfastPolicy.getHasBreakfast() : null);
            List<ContentResp.Coordinate> coordinates = address.getCoordinates();
            htHotelMthdomInfo.setLongitude(CollectionUtils.isNotEmpty(coordinates) ? coordinates.get(0).getLongitude().longValue() : null);
            htHotelMthdomInfo.setLatitude(CollectionUtils.isNotEmpty(coordinates) ? coordinates.get(0).getLatitude().longValue() : null);
            if (CollectionUtils.isNotEmpty(coordinates)) {
                String provider = coordinates.get(0).getProvider();
                switch (provider) {
                    case "GAODE":
                        htHotelMthdomInfo.setMapPointType(3);
                        break;
                    case "BAIDU":
                        htHotelMthdomInfo.setMapPointType(0);
                        break;
                    case "MAPBAR":
                        htHotelMthdomInfo.setMapPointType(4);
                        break;
                    case "GOOGLE":
                        htHotelMthdomInfo.setMapPointType(1);
                        break;
                    default:
                        htHotelMthdomInfo.setMapPointType(null);
                        break;
                }
            } else {
                htHotelMthdomInfo.setMapPointType(null);
            }
        });
        return htHotelMthdomInfo;
    }

    private HtRoomtypeMthdomInfo setHtRoomtypeMthdomInfo(RealroomContentResp.RoomContent roomContent) {
        HtRoomtypeMthdomInfo htRoomtypeMthdomInfo = new HtRoomtypeMthdomInfo();
        ContentResp.RoomBaseInfo baseInfo = roomContent.getBaseInfo();

        htRoomtypeMthdomInfo.setHotelId(parseInnId(String.valueOf(baseInfo.getHotelId())));
        htRoomtypeMthdomInfo.setRoomTypeCode(String.valueOf(baseInfo.getRealRoomId()));
        htRoomtypeMthdomInfo.setRoomTypeName(baseInfo.getName());
        htRoomtypeMthdomInfo.setRoomArea(baseInfo.getUseAbleArea());
        htRoomtypeMthdomInfo.setRoomStatus(baseInfo.getStatus());
        htRoomtypeMthdomInfo.setFloor(baseInfo.getFloor());
        htRoomtypeMthdomInfo.setMaxCheckIn(Objects.nonNull(baseInfo.getOccupancy()) ?
                baseInfo.getOccupancy().getTotal() : null);

        List<List<ContentResp.BedGroup>> bedGroups = baseInfo.getBedGroups();
        if (CollectionUtils.isEmpty(bedGroups) || CollectionUtils.isEmpty(bedGroups.get(0))) {
            htRoomtypeMthdomInfo.setBedType(null);
            htRoomtypeMthdomInfo.setBedWidth(null);
        } else {
            List<ContentResp.BedGroup> bedGroupList = bedGroups.get(0);
            if (Objects.isNull(bedGroupList.get(0))) {
                htRoomtypeMthdomInfo.setBedType(null);
                htRoomtypeMthdomInfo.setBedWidth(null);
            } else if (bedGroupList.get(0).getCount() == 2) {
                htRoomtypeMthdomInfo.setBedType(2);
            } else {
                switch (roomContent.getBaseInfo().getBedGroups().get(0).get(0).getType()) {
                    case "大床":
                        htRoomtypeMthdomInfo.setBedType(0);
                        break;
                    case "单人床":
                        htRoomtypeMthdomInfo.setBedType(1);
                        break;
                    case "双人床":
                        htRoomtypeMthdomInfo.setBedType(4);
                        break;
                    case "特大床":
                        htRoomtypeMthdomInfo.setBedType(3);
                        break;
                    default:
                        htRoomtypeMthdomInfo.setBedType(5);
                        break;
                }
                htRoomtypeMthdomInfo.setBedWidth(bedGroupList.get(0).getDescription());
            }
        }

        htRoomtypeMthdomInfo.setAddBed(baseInfo.getAllowExtraBed() ? 1 : 0);

        if (baseInfo.getWindow() == null) {
            htRoomtypeMthdomInfo.setWindowType(null);
            htRoomtypeMthdomInfo.setWindowTypeDefect(null);
        } else {
            ContentResp.Window window = baseInfo.getWindow();
            switch (window.getHasWindow()) {
                case 0:
                    htRoomtypeMthdomInfo.setWindowType(1);
                    break;
                case 1:
                    htRoomtypeMthdomInfo.setWindowType(2);
                    break;
                case 2:
                    htRoomtypeMthdomInfo.setWindowType(0);
                    break;
                default:
                    break;
            }
            htRoomtypeMthdomInfo.setWindowTypeDefect(window.getDisadvantage());
        }

        return htRoomtypeMthdomInfo;
    }

    private HtOrderSourceRecord selectSourceRecord(String bookId) {
        LambdaQueryWrapper<HtOrderSourceRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HtOrderSourceRecord::getBookId, bookId);
        return htOrderSourceRecordDao.selectOne(wrapper);
    }

    private <T, S> T getBaseRequest(Integer partnerId, String accesskey, String secretKey,
                                    String method, S req, Class<T> clazz) {
        MeituanReq meituanReq = new MeituanReq();
        meituanReq.setData(JsonUtil.toString(req));
        meituanReq.setPartnerId(partnerId);
        meituanReq.setAccesskey(accesskey);
        meituanReq.setMethod(method);
        solveReq(meituanReq, secretKey);
        MeituanResp meituanResp = meituanClient.callMeituan(meituanReq);
        if (null == meituanResp) {
            log.error("{} method error param{} result is null", method, JsonUtil.toString(req));
            throw new RuntimeException("result is null");
        }

        if (0 != meituanResp.getCode()) {
            log.error("{} method error param{} result{}", method, JsonUtil.toString(req), JsonUtil.toString(meituanResp));
            throw new RuntimeException(meituanResp.getMessage());
        }
        return JsonUtil.toObject(clazz, JsonUtil.toString(meituanResp.getResult()));
    }

    private void solveReq(MeituanReq req, String secretKey) {
        req.setVersion("1.0");
        req.setTimestamp(System.currentTimeMillis() / 1000);
        req.setNonce(new Random().nextInt(Integer.MAX_VALUE));
        // todo 正式接入时需要去掉test字段 test只再测试阶段使用
//        req.setTest("test");
        req.setSignature(calculateSignature(req, secretKey));
    }

    private String calculateSignature(MeituanReq req, String secretKey) {

        // 计算签名
        String rawSignStr = "accesskey=" + req.getAccesskey() + "&data=" + req.getData()
                + "&method=" + req.getMethod() + "&nonce=" + req.getNonce()
                + "&partnerId=" + req.getPartnerId()
                // todo 正式接入时需要去掉test字段 test只再测试阶段使用
//                + "&test=test"
                + "&timestamp=" + req.getTimestamp()
                + "&version=" + req.getVersion();
        return hmacSha1(rawSignStr, secretKey);
    }

    private String hmacSha1(String plainText, String secretKey) {
        SecretKeySpec secretKeySpec = new SecretKeySpec(org.apache.commons.codec.binary.StringUtils.getBytesUtf8(secretKey), "HmacSHA1");
        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(secretKeySpec);
            return Base64.encodeBase64String(mac.doFinal(org.apache.commons.codec.binary.StringUtils.getBytesUtf8(plainText)));
        } catch (GeneralSecurityException var5) {
            throw new IllegalArgumentException(var5);
        }
    }

    private HtHotelMthdomInfo selectHotel(String hotelId) {
        LambdaQueryWrapper<HtHotelMthdomInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HtHotelMthdomInfo::getHotelId, hotelId);
        return htHotelMthdomInfoService.getOne(wrapper);
    }
}
